package edu.lyun.trainingproject.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.lang.reflect.Constructor;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @Title: DruidDBUtil
 * @Author: CokeGGts
 * @Date: 2024-10-18 22:03:12
 * @Description: 操作数据库, 单例设计模式实现
 */
public class DruidDBUtil {
    private static DataSource dataSource;

    private static class Holder {
        private final static DruidDBUtil INSTANCE = new DruidDBUtil();
    }

    private DruidDBUtil() {
        try {
            Properties druidProperties = PropertiesConfigLoader.getInstance().loadPropertiesConfigFileToMap().get("druid");
            dataSource = DruidDataSourceFactory.createDataSource(druidProperties);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    //Java式饿汉单例
    public static DruidDBUtil getInstance() {
        return Holder.INSTANCE;
    }

    /**
     * @Title: convert
     * @Author: CokeGGts
     * @Date: 2024-10-19 13:58:23
     * @Params: [set, clazz]
     * @Return: T
     * @Description: 将ResultSet数据转换为实体类, 转化方式依赖实体类的的一个构造方法
     */
    private static <T> T convert(ResultSet set, Class<T> clazz) {
        try {
            Constructor<T> constructor = clazz.getConstructor(clazz.getConstructors()[0].getParameterTypes());   //默认获取第一个构造方法
            Class<?>[] param = constructor.getParameterTypes();  //获取参数列表
            Object[] object = new Object[param.length];  //存放参数
            for (int i = 0; i < param.length; i++) {   //是从1开始的
                object[i] = set.getObject(i + 1);
                if (object[i].getClass() != param[i])
                    throw new SQLException("错误的类型转换：" + object[i].getClass() + " -> " + param[i]);
            }
            return constructor.newInstance(object);
        } catch (ReflectiveOperationException | SQLException e) {
            System.err.println(e.getMessage());
            return null;
        }
    }

    /**
     * @Title: select
     * @Author: CokeGGts
     * @Date: 2024-10-19 14:09:33
     * @Params: [sql, params, autoCommit, clazz]
     * @Return: List<T>
     * @Description: 查询数据库
     */
    public <T> List<T> executeQuery(String sql, Object[] params, boolean autoCommit, Class<T> clazz) {
        try (Connection connection = dataSource.getConnection()) {
            ResultSet resultSet = buildDataBase(sql, params, autoCommit, connection);
            List<T> res = new ArrayList<>();
            while (resultSet.next()) {
                res.add(convert(resultSet, clazz));
            }
            return res;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    private ResultSet buildDataBase(String sql, Object[] params, boolean autoCommit, Connection connection) throws SQLException {
        connection.setAutoCommit(autoCommit);
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        for (int i = 1; i <= params.length; i++) {
            preparedStatement.setObject(i, params[i - 1]);
        }
        if (!autoCommit) connection.commit();
        return preparedStatement.executeQuery();
    }

    private void execute(String sql, Object[] params, boolean autoCommit) {
        try (Connection connection = dataSource.getConnection()) {
            buildDataBase(sql, params, autoCommit, connection);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}
